//	Project:	HexTower
//	File:		DirectGraphics_Manager.h
//	Author:		Mustafa Aldoori
//	Purpose:	Graphics Manager implementation.

#include "DirectGraphics_Manager.h"
#include "UtilityManager.h"
#include <assert.h>

#pragma warning ( disable : 4996 )

DirectGraphics_Manager DirectGraphics_Manager::m_Instance;

DirectGraphics_Manager *DirectGraphics_Manager::GetInstance( void )
{
	return &m_Instance;
}

DirectGraphics_Manager::DirectGraphics_Manager( void )
{
	m_lpDevice = NULL;
	m_lpSprite = NULL;
}

DirectGraphics_Manager::~DirectGraphics_Manager( void )
{

}

//	Purpose:	Initializes the texture manager.
bool DirectGraphics_Manager::InitGraphics_Manager(LPDIRECT3DDEVICE9 lpDevice, LPD3DXSPRITE lpSprite)
{
	m_lpDevice = lpDevice;
	m_lpDevice->AddRef();

	m_lpSprite = lpSprite;
	m_lpSprite->AddRef();

	return (m_lpDevice && m_lpSprite) ? true : false;
}

//	Purpose:	Unloads all the loaded textures and 
//				releases references to sprite and d3d devices.
void DirectGraphics_Manager::ShutdownTextureManager( void )
{
	for( unsigned int i = 0; i < m_Textures.size(); i++ )
	{
		// Remove ref.
		m_Textures[ i ].ref = 0;

		// Release the texture if it's not being used.
		Safe_Release( m_Textures[ i ].texture);
		m_Textures[ i ].filename[ 0 ] = '\0';
	}

	// Clear the list of all loaded textures.
	m_Textures.clear();

	//	Release our references to the sprite interface and d3d device
	Safe_Release( m_lpSprite );
	Safe_Release( m_lpDevice );
}

//	Purpose:	To load a texture from a file. 
int DirectGraphics_Manager::LoadTexture(const TCHAR* szFilename, DWORD dwColorkey)
{
	//	Make sure the filename is valid.
	if (!szFilename)	return -1;

	// Make sure the texture isn't already loaded.
	for (unsigned int i = 0; i < m_Textures.size(); i++)
	{
		// compare strings without caring about upper or lowercase.
		if (_tcsicmp(szFilename, m_Textures[i].filename) == 0)	// 0 means they are equal.
		{
			m_Textures[i].ref++; // add a reference to this texture.
			return i;	// return the index.
		}
	}

	// Look for an open spot.
	int nID = -1;
	for (unsigned int i = 0; i < m_Textures.size(); i++)
	{
		if (m_Textures[i].ref == 0)
		{
			nID = i;
			break;
		}
	}

	// if we didn't find an open spot, load it in a new one
	if (nID == -1)
	{
		// A temp texture object.
		TEXTURE loaded;

		// Copy the filename of the loaded texture.
		_tcscpy_s(loaded.filename, _countof(loaded.filename), szFilename);

		// Load the texture from the given file.
		HRESULT hr = 0;
		if (FAILED(hr = D3DXCreateTextureFromFileEx(m_lpDevice, szFilename, 0, 0, D3DX_DEFAULT, 0,
			D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT,
			D3DX_DEFAULT, dwColorkey, 0, 0, &loaded.texture)))
		{
			// Failed.
			TCHAR szBuffer[256] = {0};
			_stprintf_s(szBuffer, _countof(szBuffer), _T("Failed to Create Texture - %s"), szFilename); 
			MessageBox(0, szBuffer, _T("DirectGraphics_Manager Error"), MB_OK);
			return -1;
		}

		// AddRef.
		loaded.ref = 1;

		// Get surface description (to find Width/Height of the texture)
		D3DSURFACE_DESC d3dSurfDesc;
		ZeroMemory(&d3dSurfDesc, sizeof(d3dSurfDesc));

		loaded.texture->GetLevelDesc(0, &d3dSurfDesc);

		// Remember the Width and Height
		loaded.Width	= d3dSurfDesc.Width;
		loaded.Height	= d3dSurfDesc.Height;

		// Put the texture into the list.
		m_Textures.push_back(loaded);

		// Return the nID of the texture.
		return (int)m_Textures.size() - 1;
	}
	// we found an open spot
	else
	{
		// Make sure the texture has been released.
		Safe_Release(m_Textures[nID].texture);

		// Copy the filename of the loaded texture.
		_tcscpy_s(m_Textures[nID].filename, _countof(m_Textures[nID].filename), szFilename);

		// Load the texture from the given file.
		HRESULT hr = 0;
		if (FAILED(hr = D3DXCreateTextureFromFileEx(m_lpDevice, szFilename, 0, 0, D3DX_DEFAULT, 0,
			D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT,
			D3DX_DEFAULT, dwColorkey, 0, 0, &m_Textures[nID].texture)))
		{
			// Failed.
			TCHAR szBuffer[256] = {0};
			_stprintf_s(szBuffer, _countof( szBuffer ), _T("Failed to Create Texture - %s"), szFilename); 
			MessageBox(0, szBuffer, _T("DirectGraphics_Manager Error"), MB_OK);
			return -1;
		}

		// Get surface description (to find Width/Height of the texture)
		D3DSURFACE_DESC d3dSurfDesc;
		ZeroMemory(&d3dSurfDesc, sizeof(d3dSurfDesc));

		m_Textures[nID].texture->GetLevelDesc(0, &d3dSurfDesc);

		// Remember the Width and Height
		m_Textures[nID].Width	= d3dSurfDesc.Width;
		m_Textures[nID].Height	= d3dSurfDesc.Height;

		// AddRef
		m_Textures[nID].ref = 1;

		// Return the nID of the texture.
		return nID;
	}
}

//	Purpose:	Releases a reference to a given texture. When the
//				reference to the texture is zero, the texture is
//				released from memory.
void DirectGraphics_Manager::UnloadTexture( int nID )
{
	// Make sure the nID is in range.
	assert( nID > -1 && nID < ( int )m_Textures.size() && "nID is out of range" );
	
	// Remove ref.
	m_Textures[ nID ].ref--;

	// Release the texture if it's not being used.
	if( m_Textures[ nID ].ref <= 0 )
	{
		// Do a lazy delete and leave this spot empty
		Safe_Release( m_Textures[ nID ].texture );
		m_Textures[ nID ].filename[ 0 ] = '\0';
		m_Textures[ nID ].ref = 0;
	}
}

//	Purpose:	Gets the width of a specified texture.
int DirectGraphics_Manager::GetTextureWidth(int nID)
{
	// Make sure the nID is in range.
	assert(nID > -1 && nID < (int)m_Textures.size() && "nID is out of range");

	return m_Textures[nID].Width;
}

//	Purpose:	Gets the height of a specified texture.
int DirectGraphics_Manager::GetTextureHeight(int nID)
{
	// Make sure the nID is in range.
	assert(nID > -1 && nID < (int)m_Textures.size() && "nID is out of range");

	return m_Textures[nID].Height;
}

//	Purpose:	Draws a texture to the screen.
bool DirectGraphics_Manager::Draw(int nID, int nX, int nY, float fScaleX, float fScaleY,
	RECT* pSection, float fRotCenterX, float fRotCenterY, float fRotation, DWORD dwColor)
{
	// Make sure the nID is in range.
	assert(nID > -1 && nID < (int)m_Textures.size() && "nID is out of range");

	// Make sure that the texture is valid
	assert(m_Textures[nID].texture != NULL && "Attempting to draw released texture id");

	// Make sure the sprite was created and we have a valid texture.
	if (!m_lpSprite)
		return false;

	D3DXMATRIX scale;
	D3DXMATRIX rotation;
	D3DXMATRIX translate;
	D3DXMATRIX combined;

	// Initialize the Combined matrix.
	D3DXMatrixIdentity(&combined);

	// Scale the sprite.
	D3DXMatrixScaling(&scale, fScaleX, fScaleY, 1.0f);
	combined *= scale;

	// Rotate the sprite.
	D3DXMatrixTranslation(&translate, -fRotCenterX * fScaleX, -fRotCenterY * fScaleY, 0.0f);
	combined *= translate;
	D3DXMatrixRotationZ(&rotation, fRotation);
	combined *= rotation;
	D3DXMatrixTranslation(&translate, fRotCenterX * fScaleX, fRotCenterY * fScaleY, 0.0f);
	combined *= translate;

	// Translate the sprite
	D3DXMatrixTranslation(&translate, (float)nX, (float)nY, 0.0f);
	combined *= translate;

	// Apply the transform.
	m_lpSprite->SetTransform(&combined);

	// Draw the sprite.
	if (FAILED(m_lpSprite->Draw(m_Textures[nID].texture, pSection, NULL, NULL, dwColor)))
		DXError(_T("Failed to draw the texture."))

	// Move the world back to identity.
	D3DXMatrixIdentity(&combined);
	m_lpSprite->SetTransform(&combined);

	// success.
	return true;
}

#pragma warning (default : 4996)